/*
 * Copyright (C) 2008 CERNET Network Center 
 * dvping/dvmcast/hdvping/hdvmcast series 
 * 
 * Design and coding: 
 *  	Xing Li <xing@cernet.edu.cn> 
 *	Congxiao Bao <congxiao@cernet.edu.cn>
 * 	Jinpeng Jiang <jjp02@mails.tsinghua.edu.cn>
 * 
 * Contributions:
 * 
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 */


#include "FileDVSource.h"
#include <FileSystemUtil.h>
using namespace System::IO;

FileDVSource::FileDVSource(const string& file_name, bool loop)
{
	this->file_name = file_name;
	this->loop = loop;
	file_len = File::GetLength(file_name);
	if(file_len < frame_size)
	{
		cout << "The length of " << file_name << " is less than a frame." << endl;
		exit(1);
		return;
	}
	bytes_read = 0;
	
	memset(framebuf, 0, sizeof(framebuf));
	framebuf_pos = 0;

	memset(outbuf, 0, sizeof(outbuf));
	pRtpHdr = (rtp_hdr_t*)outbuf;
	rtp_trivial_hdr_t* pth = (rtp_trivial_hdr_t*)outbuf;

	pth->version = RTP_VERSION;
	pth->pt = DEFAULT_DVTS_RTP_PT;

	srand(time(NULL));
	pRtpHdr->seq = htons(rand());
	pRtpHdr->ts = htonl(rand());
	pRtpHdr->ssrc = htonl(rand());

	frame_count = 0;

	frame_time = 1000000.0 / DEFAULT_FPS_NTSC;
	byte_rate = 2.0 * DEFAULT_FPS_NTSC * NTSC_FRAME_SIZE / 1000000.0;
	
	bytes_sent = 0;
	packet_start_time = 0;

	cur_frame = -1;
	frame_count = 0;

}
bool FileDVSource::Init()
{
	fin.open(file_name.c_str(), ios::in | ios::binary);
	if(!fin)
	{
		cout << "Open file: " << file_name << " failed." << endl;
		return false;
	}
	start_time = DateTime::Now().GetTickCount();
	frame_count = 0;
	framebuf_pos = 0;

	bytes_sent = 0;
	packet_start_time = start_time;

	cur_frame = -1;
	frame_count = 0;

	return true;
}
DVPacket FileDVSource::GetPacket()
{
	DVPacket pkt;
	pkt.length = sizeof(rtp_hdr_t);
	bool next_frame = false;
	for(int dif = 0; dif < DIF_BLOCK_PER_PACKET; ++dif)
	{
		if(cur_frame < 0 || framebuf_pos + DIF_BLOCK_SIZE > frame_size)
		{
			if(framebuf_count == 0)
			{
				while(framebuf_count < framebuf_capacity)
				{
					int64_t bytes_remain = file_len - bytes_read;
					if(bytes_remain < frame_size)
					{
						if(loop)
						{
							fin.seekg(0, ios::beg);
							fin.clear();
							bytes_read = 0;
							continue;
						}
						else
						{
							quitThread = true;
							pkt.length = 0;
							pkt.pdata = NULL;
							return pkt;
						}
					}
					bytes_remain = bytes_remain / frame_size * frame_size;
					int64_t framebuf_left = (framebuf_capacity - framebuf_count) * frame_size;
					int64_t bytes_to_read = (bytes_remain > framebuf_left ? framebuf_left : bytes_remain);
					fin.read(framebuf + framebuf_size - framebuf_left, (std::streamsize)bytes_to_read);
					framebuf_count += (int)(bytes_to_read / frame_size);
					bytes_read += bytes_to_read;
				}
				cur_frame = framebuf_count - 1;
			}
			--framebuf_count;
			++cur_frame;
			cur_frame %= framebuf_capacity;
			framebuf_pos = 0;

			// control the frame rate
			++frame_count;
			int64_t now = DateTime::Now().GetTickCount();
			int64_t expected_time = start_time + (int64_t)(frame_count * frame_time);
			if(frame_count == 1000)
			{
				//cout << "frame rate:" << frame_count * 1000000.0/ (now - start_time) << endl;
				frame_count = 0;
				start_time = now;
			}
			if(now < expected_time)
			{
				if(framebuf_count < framebuf_capacity - 1)
				{
					while(framebuf_count < framebuf_capacity - 1)
					{
						int64_t bytes_remain = file_len - bytes_read;
						if(bytes_remain < frame_size)
						{
							if(loop)
							{
								fin.seekg(0, ios::beg);
								fin.clear();
								bytes_read = 0;
								continue;
							}
							else
							{
								break;
							}
						}
						fin.read(framebuf + ((cur_frame + framebuf_count + 1) % framebuf_capacity) * frame_size, frame_size);
						bytes_read += frame_size;
						++framebuf_count;
						if(DateTime::Now().GetTickCount() > expected_time)
							break;
					}
					now = DateTime::Now().GetTickCount();
					if(now < expected_time)
					{
						Thread::Sleep(expected_time - now);
					}
				}
				else
				{
					Thread::Sleep(expected_time - now);
				}
			}
			static int output_count = 0;
			++output_count;
			if(output_count == 30)
			{
		//		cout << "framebuf_count:" << framebuf_count << endl;
				output_count = 0;
			}
			packet_start_time = DateTime::Now().GetTickCount();
			bytes_sent = 0;
		}
		u_int32_t* difblock = (u_int32_t*)(outbuf + sizeof(rtp_hdr_t) + dif * DIF_BLOCK_SIZE);
		memcpy((char*)difblock, framebuf + cur_frame * frame_size + framebuf_pos, DIF_BLOCK_SIZE);
		framebuf_pos += DIF_BLOCK_SIZE;
		pkt.length += DIF_BLOCK_SIZE;
		u_int8_t dbn, dseq, seq, sct;
		dbn  = (u_int8_t)(ntohl(difblock[0]) >>  8) & 0xff;
		dseq = (u_int8_t)(ntohl(difblock[0]) >> 20) & 0xf;
		seq  = (u_int8_t)(ntohl(difblock[0]) >> 24) & 0xf;
		sct  = (u_int8_t)(ntohl(difblock[0]) >> 29) & 0x7;
		if(sct == SCT_VIDEO && dseq == (DIFSEQ_NUM_NTSC - 1) && dbn == 134)
		{
			next_frame = true;
			break;
		}
	}
	
	pktQueue.GetFreePacket(pkt);
	memcpy(pkt.pdata, outbuf, sizeof(outbuf));
	pkt.srcep.Address() = IPAddress::Null;
	
	bytes_sent += pkt.length;
	int64_t now = DateTime::Now().GetTickCount();
	int64_t expected_time = packet_start_time + (int64_t)(bytes_sent / byte_rate);
	if(now < expected_time)
	{
		Thread::Sleep(expected_time - now);
	}

	pRtpHdr->seq = htons(ntohs(pRtpHdr->seq) + 1);
	if(next_frame)
		pRtpHdr->ts = htonl(ntohl(pRtpHdr->ts) + 3003);
	return pkt;
}
bool FileDVSource::Cleanup()
{
	fin.close();
	return true;
}

double FileDVSource::GetTraffic()
{
	return byte_rate * 0.9;
}

